package com.hp.bon.sgw.convert;

import com.hp.bon.sgw.bean.ProtocalConvertResult;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.domain.ConvMapInfo;
import com.hp.bon.sgw.util.dcc.AvpDef;
import com.hp.bon.sgw.util.dcc.DccAvpHelper;
import com.hp.ocs.diameter.message.AVP;
import com.hp.ocs.diameter.message.AVP_Grouped;
import com.hp.ocs.diameter.message.DictionaryManager;
import com.hp.ocs.diameter.message.InvalidAVPLengthException;
import com.hp.ocs.diameter.message.InvalidAddressTypeException;
import com.hp.ocs.diameter.message.Message;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DataExchangerImpl implements BONServiceDataExchanger {
	private static DataExchangerImpl instance;
	private static final DictionaryManager _dic = DictionaryManager.getInstance();

	private static final Logger logger = LoggerFactory.getLogger(DataExchangerImpl.class);

	public static DataExchangerImpl getInstance() {
		if (instance == null) {
			instance = new DataExchangerImpl();
		}
		return instance;
	}

	public ProtocalConvertResult<AVP> converToCCRRequest(String serviceCtxID, Element requestData) {
		return transtoAVP(serviceCtxID, requestData);
	}

	public ProtocalConvertResult<AVP> convertToCCAResult(String serviceCtxID, Element request) {
		return transtoAVP(serviceCtxID, request);
	}

	public ProtocalConvertResult<Element> convertFromCCRRequest(String serviceCtxID, AVP request) {
		Map allAVPMappings = Xml2AVPConvetor.getInstance().findAVPToXMLMappings(serviceCtxID, null);
		if (allAVPMappings == null) {
			ProtocalConvertResult cvtRst = new ProtocalConvertResult();
			String errMsg = "can't find AVPMappings for serviceContextID:" + serviceCtxID;
			logger.warn(errMsg);
			cvtRst.setRetCode(401);
			cvtRst.setErrMsg("No XML-AVP Convert Rule Defined");
			return cvtRst;
		}
		return transtoXML(allAVPMappings, request);
	}

	public ProtocalConvertResult<Element> convertFromCCAResult(String serviceCtxID, AVP response) {
		Map allAVPMappings = Xml2AVPConvetor.getInstance().findAVPToXMLMappings(serviceCtxID, null);
		if (allAVPMappings == null) {
			ProtocalConvertResult cvtRst = new ProtocalConvertResult();
			String errMsg = "can't find AVPMappings for serviceContextID:" + serviceCtxID;
			logger.warn(errMsg);
			cvtRst.setRetCode(401);
			cvtRst.setErrMsg("No XML-AVP Convert Rule Defined");
			return cvtRst;
		}
		return transtoXML(allAVPMappings, response);
	}

	private static AvpDef makeAvpDef(AVP avp, Map<Integer, ConvMapInfo> allAVPMapInfo) throws InvalidAVPLengthException {
		ConvMapInfo convMapInfo = (ConvMapInfo) allAVPMapInfo.get(Integer.valueOf(avp.code()));
		if (convMapInfo == null) {
			logger.warn("Cannot find the right XML-Tag-Name for AVP:" + avp.code() + ";" + avp.vendorID());
			return null;
		}
		AvpDef AvpDef = new AvpDef();
		AvpDef.setName(convMapInfo.getXmlTag());

		if (convMapInfo.getAvpType().equalsIgnoreCase("Grouped")) {
			AVP[] group = new AVP_Grouped(avp).queryAVPs();
			if ((group == null) || (group.length <= 0)) {
				return null;
			}
			List subAvpDefs = new ArrayList();
			AvpDef __tmp = null;
			for (AVP obj : group) {
				__tmp = makeAvpDef(obj, allAVPMapInfo);
				if (__tmp != null)
					subAvpDefs.add(__tmp);
			}
			if (subAvpDefs.size() <= 0) {
				return null;
			}
			AvpDef.setAvps(subAvpDefs);
			return AvpDef;
		}
		try {
			AvpDef.setValue(_dic.getAvpValueAsString(avp));
			return AvpDef;
		} catch (IOException e) {
			logger.error("Parsing AVP IO Error for AVP:" + avp.code() + ";" + avp.vendorID(), e);
		} catch (InvalidAddressTypeException e) {
			logger.error("Parsing AVP InvalidAddressType Error for AVP:" + avp.code() + ";" + avp.vendorID(), e);
		}
		return null;
	}

	private AVP makeAVP(AvpDef AvpDef, Map<String, ConvMapInfo> allXmlMapInfo) {
		ConvMapInfo convMapInfo = (ConvMapInfo) allXmlMapInfo.get(AvpDef.getName());
		if (convMapInfo == null) {
			throw new RuntimeException("Cannot find the right AVP-Name for XML-Tag-Name:" + AvpDef.getName());
		}
		List subAvps = AvpDef.getAvps();
		AVP avp = null;
		if ((subAvps != null) && (subAvps.size() > 0)) {
			Iterator it = subAvps.iterator();
			List avps = new ArrayList();
			while (it.hasNext()) {
				avp = makeAVP((AvpDef) it.next(), allXmlMapInfo);
				avps.add(avp);
			}
			return _dic.makeAVP(convMapInfo.getAvpCode(), convMapInfo.getAvpName(), avps.toArray(new AVP[avps.size()]));
		}
		String value = AvpDef.getValue();
		return _dic.makeAVP(convMapInfo.getAvpCode(), convMapInfo.getAvpName(), value);
	}

	private ProtocalConvertResult<AVP> transtoAVP(String serviceContextId, Element xmlData) {
		ProtocalConvertResult cvtRst = new ProtocalConvertResult();
		Map allAVPMappings = Xml2AVPConvetor.getInstance().findXMLToAVPMappings(serviceContextId, null);

		if (allAVPMappings == null) {
			String errMsg = "can't find AVPMappings for serviceContextID:" + serviceContextId;
			logger.warn(errMsg);
			cvtRst.setErrMsg(errMsg);
			cvtRst.setRetCode(11106);
			return cvtRst;
		}
		try {
			Element e = xmlData;
			AvpDef AvpDef = makeAvpDef(e);
			if (AvpDef != null) {
				AVP resultAVP = makeAVP(AvpDef, allAVPMappings);
				if (resultAVP != null) {
					cvtRst.setRetCode(0);
					cvtRst.setResult(resultAVP);
				}
			} else {
				cvtRst.setRetCode(11104);
				cvtRst.setErrMsg("no avp converted from xml ");
			}

			return cvtRst;
		} catch (Exception e) {
			e.printStackTrace();
			cvtRst.setRetCode(11106);
			cvtRst.setErrMsg("convert err:" + e.toString());
		}
		return cvtRst;
	}

	private AvpDef makeAvpDef(Element e) {
		AvpDef AvpDef = new AvpDef();
		AvpDef.setName(e.getName());
		Iterator iterator = e.elementIterator();

		if (iterator.hasNext()) {
			AvpDef subAvp = null;
			List subAvpDefs = new ArrayList();
			while (iterator.hasNext()) {
				subAvp = makeAvpDef((Element) iterator.next());
				if (subAvp != null)
					subAvpDefs.add(subAvp);
			}
			if (subAvpDefs.size() > 0) {
				AvpDef.setAvps(subAvpDefs);
				return AvpDef;
			}
			return null;
		}

		String value = e.getText();

		if ((value != null) && (!value.isEmpty())) {
			AvpDef.setValue(value.trim());
			return AvpDef;
		}
		logger.warn("The value is null for XML-Tag:" + e.getName());

		return null;
	}

	private static Element makeXML(AvpDef AvpDef) {
		List<AvpDef> subElements = AvpDef.getAvps();
		Element e = DocumentHelper.createElement(AvpDef.getName());
		// group element
		if (null != subElements && subElements.size() > 0) {
			for (AvpDef obj : subElements) {
				e.add(makeXML(obj));
			}
			return e;
		}
		// Single element
		e.setText(AvpDef.getValue());
		return e;
	}

	public ProtocalConvertResult<Element> transtoXML(Map<Integer, ConvMapInfo> allAVPMappings, AVP avp) {
		ProtocalConvertResult cvtRst = new ProtocalConvertResult();
		Element transData = null;
		try {
			AvpDef AvpDef = makeAvpDef(avp, allAVPMappings);
			if (AvpDef != null) {
				transData = makeXML(AvpDef);
			}
			if (transData == null)
				cvtRst.setRetCode(401);
			else {
				cvtRst.setRetCode(0);
			}
			cvtRst.setResult(transData);
			return cvtRst;
		} catch (Exception e) {
			cvtRst.setRetCode(11106);
			cvtRst.setErrMsg("convert err:" + e.toString());
		}
		return cvtRst;
	}

	public static ProtocalConvertResult<Element> transtoXML(Map<Integer, ConvMapInfo> allAVPMappings, Message message) {
		AVP sgwRootAVP = DccAvpHelper.createSGWRootAVPFromMessage(message);
		ProtocalConvertResult cvtRst = new ProtocalConvertResult();
		Element transData = null;
		try {
			AvpDef AvpDef = makeAvpDef(sgwRootAVP, allAVPMappings);
			if (AvpDef != null) {
				transData = makeXML(AvpDef);
			}
			if (transData == null)
				cvtRst.setRetCode(401);
			else {
				cvtRst.setRetCode(0);
			}
			cvtRst.setResult(transData);
			return cvtRst;
		} catch (Exception e) {
			cvtRst.setRetCode(11106);
			cvtRst.setErrMsg("convert err:" + e.toString());
		}
		return cvtRst;
	}

	public void convertCCAToXML(String serviceCtxID, Message answer, CallParam callResult) {
		int rc = DccAvpHelper.getCCAMessageResultCode(answer);

		if ((serviceCtxID.contains(".OCS")) && (((rc >= 2000) && (rc <= 2999)) || ((rc >= 4201) && (rc <= 4221)))) {
			callResult.setRetCode(rc);
			doStandardAVPConvert(serviceCtxID, answer, callResult);
			return;
		}
		switch (rc) {
		case 2001:
			doStandardAVPConvert(serviceCtxID, answer, callResult);
			return;
		}

		callResult.setRetCode(11206);
		callResult.setResultMsg("BON 返回错误:" + rc);
	}

	private void doStandardAVPConvert(String serviceCtxID, Message answer, CallParam callResult) {
		AVP sgwRootAVP = DccAvpHelper.createSGWRootAVPFromMessage(answer);

		ProtocalConvertResult resultCvt = getInstance().convertFromCCAResult(serviceCtxID, sgwRootAVP);
		if (resultCvt.getRetCode() == 0) {
			callResult.setResponse(resultCvt.getResult());
			callResult.setResultMsg("SUCCESS");
		} else {
			callResult.setRetCode(11106);
			callResult.setResultMsg(resultCvt.getErrMsg() != null ? resultCvt.getErrMsg() : "CONVERT RESPONSE AVP ERR");
		}
	}
}